import json
from typing import Dict, List, Optional, Tuple
from datetime import datetime, timedelta
import pandas as pd
import numpy as np
from vnpy.trader.object import BarData, Exchange, Interval
import os
import sys

# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 获取项目根目录
project_root = os.path.dirname(os.path.dirname(current_dir))
# 将项目根目录添加到 sys.path
sys.path.append(project_root)

from portfolio_trader.advisor.data_utils import convert_to_bar_data, load_market_data
from portfolio_trader.advisor.engine import AdvisorTemplate
from portfolio_trader.strategy.loader import load_strategy
from portfolio_trader.engine.position_manager import PositionManager
from portfolio_trader.engine.asset_manager import AssetManager  # 引入资产管理器
from portfolio_trader.engine.stock_collection_manager import StockCollection
from portfolio_trader.datafeed.data_loader import PortfolioDataLoader  # 引入数据加载器
from vnpy_ctastrategy.backtesting import BacktestingEngine  # 引入回测引擎
from portfolio_trader.advisor.data_class import Decision, DecisionEncoder


class StrategyAdvisor(AdvisorTemplate):
    """基于多交易策略的投资顾问实现"""

    def __init__(self, asset_manager: AssetManager, position_manager: PositionManager, 
                 stock_collection : StockCollection,
                 data_loader: Optional[PortfolioDataLoader] = None ):
        self.strategies = {}  # 支持多个策略
        self._current_capital = 0.0
        self._positions: Dict[str, int] = {}
        self._market_data: Optional[pd.DataFrame] = None
        self._bars: List[BarData] = []
        self._parameters: Dict = {}
        self.position_manager = position_manager
        self.asset_manager = asset_manager
        self.data_loader = data_loader or PortfolioDataLoader()  # 使用传入的data_loader或创建新实例
        self.collection: List[str] = stock_collection.collection  # 用户收藏的股票代码列表，由外部传入
        self.settings_file = os.path.join(project_root, "portfolio_trader/advisor", "setting.json")  # 设置文件路径
        self.backtesting_engine = BacktestingEngine()  # 初始化回测引擎
        self.strategy_evaluation = {}  # 新增策略评价属性

    def on_init(self):
        """初始化"""
        if "strategies" in self._parameters:
            for strategy_name in self._parameters["strategies"]:
                strategy_class = load_strategy(strategy_name)
                print(f"加载的策略类: {strategy_class}")
                # 提供 vt_symbol 和 setting 参数
                vt_symbol = self._parameters.get("vt_symbol")
                setting = {}
                self.strategies[strategy_name] = strategy_class(None, strategy_name, vt_symbol, setting)
                self.strategies[strategy_name].on_init()

    def on_data(self, data: Dict):
        """数据更新回调"""
        if "market_data" in data:
            self._market_data = data["market_data"]
            self._bars = convert_to_bar_data(data["market_data"])

        if "account_info" in data:
            account = data["account_info"]
            self._current_capital = account.get("available", 0.0)

    def get_positions_from_engine(self, engine: PositionManager):
        self._positions = {symbol: position["volume"] for symbol, position in engine.get_positions().items()}
        self.position_manager = engine

    def generate_advice(self) -> Dict:
        """生成投资建议"""
        decisions = self.make_decisions()
        
        # 评价策略表现
        self.strategy_evaluation = self.evaluate_strategies()

        return {
            "decisions": decisions,
            "formatted_advice": self.format_advice(decisions),
            # "evaluation": self.strategy_evaluation  # 新增评价结果
        }

    def get_parameters(self) -> Dict:
        """获取参数"""
        return self._parameters.copy()

    def set_parameters(self, params: Dict):
        """设置参数"""
        self._parameters.update(params)
        if "strategies" in params:
            self.on_init()  # 重新初始化策略

    def calculate_strategy_weights(self, symbol: str, strategies: Dict[str, object]) -> Dict[str, float]:
        """计算策略权重，基于回测表现（如 total_return）"""
        weights = {}
        total_return = 0.0

        for strategy_name, strategy in strategies.items():
            # 使用回测引擎计算策略的回测表现
            statistics = self.backtesting_engine.calculate_statistics(df=None, output=False)
            total_return += statistics.get("total_return", 0)

        for strategy_name, strategy in strategies.items():
            statistics = self.backtesting_engine.calculate_statistics(df=None, output=False)
            strategy_return = statistics.get("total_return", 0)
            if total_return > 0:
                weights[strategy_name] = strategy_return / total_return
            else:
                weights[strategy_name] = 1.0 / len(strategies)

        return weights

    def generate_signals(self) -> List[Tuple[str, float]]:
        """生成交易信号，综合多个策略的信号"""
        signals = []

        if not self._bars:
            print("没有可用的 K 线数据。")
            return signals

        latest_bar = self._bars[-1]

        # 为每个策略生成信号
        strategy_signals = {}
        for strategy_name, strategy in self.strategies.items():
            strategy.on_bar(latest_bar)
            buy_signal = hasattr(strategy, 'buy_signal') and strategy.buy_signal
            sell_signal = hasattr(strategy, 'sell_signal') and strategy.sell_signal

            if buy_signal:
                strategy_signals.setdefault(strategy_name, []).append(("buy", latest_bar.close_price))
                print(f"策略 {strategy_name} 生成买入信号，价格 {latest_bar.close_price}")
            if sell_signal:
                strategy_signals.setdefault(strategy_name, []).append(("sell", latest_bar.close_price))
                print(f"策略 {strategy_name} 生成卖出信号，价格 {latest_bar.close_price}")

        # 计算策略权重
        weights = self.calculate_strategy_weights(latest_bar.symbol, self.strategies)

        # 处理冲突信号
        buy_weight = 0
        sell_weight = 0
        for strategy_name, weight in weights.items():
            for signal in strategy_signals.get(strategy_name, []):
                action, price = signal
                if action == "buy":
                    buy_weight += weight
                elif action == "sell":
                    sell_weight += weight

        final_signals = []
        if buy_weight > sell_weight:
            # 最终信号为买入
            for strategy_name, weight in weights.items():
                for signal in strategy_signals.get(strategy_name, []):
                    action, price = signal
                    if action == "buy":
                        final_signals.append((action, price, weight))
        elif sell_weight > buy_weight:
            # 最终信号为卖出
            for strategy_name, weight in weights.items():
                for signal in strategy_signals.get(strategy_name, []):
                    action, price = signal
                    if action == "sell":
                        final_signals.append((action, price, weight))
        else:
            # 权重相等，不生成信号
            pass

        return final_signals

    def make_decisions(self) -> List[Decision]:
        self.get_positions_from_engine(self.position_manager)
        signals = self.generate_signals()
        decisions = []
        positions = self.position_manager.get_positions()

        for signal in signals:
            action, price, weight = signal
            # 获取当前股票代码（假设market_data只有单个股票数据）
            symbol = self._market_data["symbol"].astype(str).iloc[-1].zfill(6)
            
            max_quantity = int(self._current_capital * 0.2 * weight / price)
            if max_quantity == 0:
                continue

            current_position = positions.get(symbol, {}).get("volume", 0)
            if action == "buy" and current_position >= max_quantity * 3:
                continue
            if action == "sell" and current_position == 0:
                continue

            price_range = (round(price * 0.98, 2), round(price * 1.02, 2))
            reason = f"策略信号：{action.upper()}，当前持仓：{current_position}股，权重：{weight:.2f}"

            quantity = min(max_quantity, current_position) if action == "sell" else max_quantity
            # 传入昨天收盘价
            decisions.append(Decision(
                symbol=symbol,
                action=action,
                quantity=quantity,
                price_range=price_range,
                reason=reason
            ))

        return decisions
    def format_advice(self, decisions: List[Decision]) -> Dict:
        """格式化输出建议"""
        advice = {
            "time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            "buy_decisions": [],
            "sell_decisions": [],
            "current_capital": self._current_capital,
            "used_capital": 0,
            "evaluation": self.strategy_evaluation
        }

        buy_decisions = [d for d in decisions if d.action == "buy"]
        sell_decisions = [d for d in decisions if d.action == "sell"]

        if buy_decisions:
            advice["buy_decisions"] = [
                {
                    "symbol": decision.symbol,
                    "quantity": decision.quantity,
                    "price_range": [decision.price_range[0], decision.price_range[1]],
                    "reason": decision.reason
                }
                for decision in buy_decisions
            ]
            advice["used_capital"] = sum(d.quantity * d.price_range[0] for d in buy_decisions)

        if sell_decisions:
            advice["sell_decisions"] = [
                {
                    "symbol": decision.symbol,
                    "quantity": decision.quantity,
                    "price_range": [decision.price_range[0], decision.price_range[1]],
                    "reason": decision.reason
                }
                for decision in sell_decisions
            ]

        if not buy_decisions and not sell_decisions:
            symbol = self._market_data["symbol"].iloc[-1]
            advice["message"] = f"{symbol}：建议今日持仓"

        return advice

    def generate_advice_for_stock(self, symbol: str, strategies: List[str]) -> Dict:
        """
        为指定股票生成投资建议
        参数：
            symbol: 股票代码
            strategies: 策略名称列表
        返回：
            投资建议字典，包含决策列表和格式化建议
        """
        # 更新策略列表
        self.set_parameters({"strategies": strategies, "vt_symbol": symbol})

        # 计算时间范围
        today = datetime.now()
        end_date = today - timedelta(days=1)
        start_date = end_date - timedelta(days=365)

        # 加载市场数据
        try:
            # 获取交易所
            exchange = self.data_loader._get_exchange(symbol)
            
            # 加载日线数据
            bars = self.data_loader.load_daily_data(
                symbol=symbol,
                start_date=start_date,
                end_date=end_date
            )
            
            if not bars:
                print("未获取到日线数据")
                return {}
            
            # 反转 bars 列表，使其从旧到新排列
            bars = bars[::-1]
            
            # 转换为DataFrame并处理时区
            historical_data = pd.DataFrame([{
                "symbol": bar.symbol,
                "exchange": bar.exchange.value,
                "datetime": bar.datetime.replace(tzinfo=None),  # 去除时区
                "interval": bar.interval.value,
                "volume": bar.volume,
                "open_price": bar.open_price,
                "high_price": bar.high_price,
                "low_price": bar.low_price,
                "close_price": bar.close_price
            } for bar in bars])
            
            # 加载估值数据
            valuation_data = self.data_loader.load_valuation_data(
                symbol=symbol,
                start_date=start_date,
                end_date=end_date
            )
            
            # 处理估值数据时区
            if not valuation_data.empty:
                valuation_data['datetime'] = valuation_data['datetime'].dt.tz_localize(None)  # 去除时区

            # 合并数据
            if not valuation_data.empty:
                historical_data = historical_data.merge(
                    valuation_data,
                    on=["symbol", "exchange", "datetime"],
                    how="left"
                )

        except Exception as e:
            print(f"获取数据失败：{e}")
            return {}

        # 更新市场数据
        market_data = {"market_data": historical_data}
        self.on_data(market_data)

        # 更新账户信息
        account_info = {
            "account_info": {
                "available": self.asset_manager.get_available_capital(),
            }
        }
        self.on_data(account_info)

        # 生成投资建议
        advice = self.generate_advice()
        return advice

    def generate_advice_for_date(self, date: datetime, strategies: List[str]) -> Dict[str, Dict]:
        """
        为指定日期的收藏列表中的每个股票生成投资建议
        参数：
            date: 指定日期
            strategies: 策略名称列表
        返回：
            一个字典，键为股票代码，值为对应的投资建议
        """
        # 计算时间范围
        end_date = date - timedelta(days=1)
        start_date = end_date - timedelta(days=365)

        # 更新策略列表
        self.set_parameters({"strategies": strategies})

        # 使用已经在设置中的 tushare_token
        advice_collection = {}
        if not self.collection:
            print("收藏列表为空，无法生成投资建议")
            return {}

        for symbol in self.collection:
            print(f"正在为 {symbol} 生成指定日期 {date} 的投资建议...")

            # 加载市场数据
            try:
                # 获取交易所
                exchange = self.data_loader._get_exchange(symbol)
                
                # 加载日线数据
                bars = self.data_loader.load_daily_data(
                    symbol=symbol,
                    start_date=start_date,
                    end_date=end_date
                )

                if not bars:
                    print(f"未获取到 {symbol} 的日线数据")
                    continue
                
                bars = bars[::-1]
                
                # 转换为DataFrame并处理时区
                historical_data = pd.DataFrame([{
                    "symbol": bar.symbol,
                    "exchange": bar.exchange.value,
                    "datetime": bar.datetime.replace(tzinfo=None),  
                    "interval": bar.interval.value,
                    "volume": bar.volume,
                    "open_price": bar.open_price,
                    "high_price": bar.high_price,
                    "low_price": bar.low_price,
                    "close_price": bar.close_price
                } for bar in bars])
                
                # 加载估值数据
                valuation_data = self.data_loader.load_valuation_data(
                    symbol=symbol,
                    start_date=start_date,
                    end_date=end_date
                )
                
                # 处理估值数据时区
                if not valuation_data.empty:
                    valuation_data['datetime'] = valuation_data['datetime'].dt.tz_localize(None)  # 去除时区

                # 合并数据
                if not valuation_data.empty:
                    historical_data = historical_data.merge(
                        valuation_data,
                        on=["symbol", "exchange", "datetime"],
                        how="left"
                    )

            except Exception as e:
                print(f"获取数据失败：{e}")
                continue

            # 更新市场数据
            market_data = {"market_data": historical_data}
            self.on_data(market_data)

            # 更新账户信息
            account_info = {
                "account_info": {
                    "available": self.asset_manager.get_available_capital(),
                }
            }
            self.on_data(account_info)

            # 生成投资建议
            advice = self.generate_advice()
            advice_collection[symbol] = advice

        return advice_collection

    def generate_advice_for_collection(self, strategies: List[str]) -> Dict[str, Dict]:
        """
        为收藏列表中的每个股票生成投资建议
        参数：
            strategies: 策略名称列表
        返回：
            一个字典，键为股票代码，值为对应的投资建议
        """
        # 使用已经在设置中的 tushare_token
        advice_collection = {}
        if not self.collection:
            print("收藏列表为空，无法生成投资建议")
            return {}
            
        for symbol in self.collection:
            print(f"正在为 {symbol} 生成投资建议...")
            advice = self.generate_advice_for_stock(symbol, strategies)
            advice_collection[symbol] = advice
        return advice_collection

    def evaluate_strategies(self) -> Dict:
        """基于历史数据回测评估策略表现"""
        evaluation = {
            "overall": {},  # 整体评价指标
            "strategies": {}  # 每个策略的详细评价
        }
        end_date = datetime.now()          # 当前日期
        start_date = end_date - timedelta(days=365)   # 回测起始日期

        # 为每个策略计算评价指标
        for strategy_name, strategy in self.strategies.items():
            strategy_evaluation = {
                "total_return": 0.0,
                "annualized_return": 0.0,
                "max_drawdown": 0.0,
                "volatility": 0.0,
                "sharpe_ratio": 0.0,
                "sortino_ratio": 0.0,
                "calmar_ratio": 0.0,
                "trade_count": 0
            }

            # 加载历史数据并运行策略
            portfolio_value = []
            trade_log = []
            for symbol in self.collection:
                # 加载历史数据
                bars = self.data_loader.load_daily_data(
                    symbol=symbol,
                    start_date=start_date,
                    end_date=end_date
                )
                if not bars:
                    continue
                
                bars = bars[::-1]
                
                # 初始化策略状态
                strategy.on_init()
                initial_capital = 1e6  # 初始资金
                capital = initial_capital
                position = 0

                # 模拟交易
                for i, bar in enumerate(bars):
                    strategy.on_bar(bar)

                    # 检查信号并执行交易
                    if hasattr(strategy, 'buy_signal') and strategy.buy_signal:
                        # 买入逻辑
                        shares = int(capital * 0.1 / bar.close_price)
                        if shares > 0:
                            position += shares
                            capital -= shares * bar.close_price
                            trade_log.append(("buy", bar.close_price, bar.datetime, shares))

                    if hasattr(strategy, 'sell_signal') and strategy.sell_signal:
                        # 卖出逻辑
                        if position > 0:
                            capital += position * bar.close_price
                            trade_log.append(("sell", bar.close_price, bar.datetime, position))
                            position = 0

                    # 记录每日资产
                    portfolio_value.append(capital + position * bar.close_price)

            # 计算评价指标
            if portfolio_value:
                # 转换为收益率序列
                returns = np.diff(portfolio_value) / portfolio_value[:-1]

                # 总收益率
                strategy_evaluation["total_return"] = (portfolio_value[-1] - initial_capital) / initial_capital * 100

                # 年化收益率
                years = (end_date - start_date).days / 365
                strategy_evaluation["annualized_return"] = (1 + strategy_evaluation["total_return"] / 100) ** (1 / years) - 1

                # 最大回撤
                peak = initial_capital
                drawdowns = []
                for value in portfolio_value:
                    if value > peak:
                        peak = value
                    drawdown = (peak - value) / peak
                    drawdowns.append(drawdown)
                strategy_evaluation["max_drawdown"] = max(drawdowns) * 100

                # 波动率
                strategy_evaluation["volatility"] = np.std(returns) * np.sqrt(252) * 100  # 年化波动率

                # 夏普比率
                risk_free_rate = 0.03
                excess_returns = returns - risk_free_rate / 252
                strategy_evaluation["sharpe_ratio"] = np.mean(excess_returns) / np.std(excess_returns) * np.sqrt(252)

                # 索提诺比率
                downside_returns = returns[returns < risk_free_rate / 252]
                downside_std = np.std(downside_returns)
                strategy_evaluation["sortino_ratio"] = np.mean(excess_returns) / downside_std * np.sqrt(252)

                # 卡玛比率
                strategy_evaluation["calmar_ratio"] = strategy_evaluation["annualized_return"] / (strategy_evaluation["max_drawdown"] / 100)

                # 交易次数
                strategy_evaluation["trade_count"] = len(trade_log) // 2  # 每次买卖算一次交易

            # 存储策略评价
            evaluation["strategies"][strategy_name] = strategy_evaluation
            
            return evaluation
        
# def main():
#     # 初始化资产管理器
#     asset_manager = AssetManager(initial_capital=1000000.0)
#     data_loader = PortfolioDataLoader()
#     stock_collection = StockCollection(data_loader)

#     # 初始化持仓管理器
#     position_manager = PositionManager(asset_manager)
#     position_manager.positions = {
#         "600000.SSE": {
#             "volume": 10000,
#             "available": 10000,
#             "cost": 0.0,
#             "current_price": 0.0,
#             "market_value": 0.0,
#             "pnl": 0.0,
#             "pnl_pct": 0.0,
#             "daily_pnl": 0.0,
#             "daily_pnl_pct": 0.0,
#             "name": "",
#             "exchange": ""
#         }
#     }
    
#     # 初始化投资顾问
#     strategy_advisor = StrategyAdvisor(asset_manager, position_manager, stock_collection)

#     # 添加收藏的股票代码
#     stock_collection.add_to_collection("600000")

#     # 生成收藏列表中所有股票的投资建议
#     advice_collection = strategy_advisor.generate_advice_for_collection(
#         strategies=[ "AtrRsiStrategy"],
#     )

#     # 打印每个股票的投资建议（JSON 格式）
#     for symbol, advice in advice_collection.items():
#         print(f"\n=== {symbol} 的投资建议 ===")
#         print(json.dumps(advice, indent=4, ensure_ascii=False,cls=DecisionEncoder))
    
#     # # 指定日期生成投资建议
#     # specific_date = datetime(2024, 10, 10)
#     # advice_for_date = strategy_advisor.generate_advice_for_date(
#     #     date=specific_date,
#     #     strategies=["AtrRsiStrategy"]
#     # )

#     # # 打印指定日期的投资建议
#     # for symbol, advice in advice_for_date.items():
#     #     print(f"\n==={symbol} 的 {specific_date.strftime('%Y-%m-%d')} 投资建议 ===")
#     #     print(json.dumps(advice, indent=4, ensure_ascii=False, cls=DecisionEncoder))


# if __name__ == "__main__":
#     main()